package com.gitee.dolt.boot.handler;

import cn.hutool.core.util.StrUtil;
import com.gitee.dolt.core.base.Result;
import com.gitee.dolt.core.exception.BizException;
import com.gitee.dolt.core.exception.ValidParamData;
import com.gitee.dolt.core.exception.ValidParamException;
import com.gitee.dolt.core.exception.code.ExceptionCode;
import com.gitee.dolt.core.util.SpringUtils;
import io.lettuce.core.RedisException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.List;


/**
 * @author LYH
 */
@Slf4j
public abstract class BaseGlobalExceptionHandler {


    /**
     * 业务异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(BizException.class)
    public Result handlerBizException(BizException ex) {
        String msg = ex.getMessage();
        if (ex instanceof ValidParamException) {
            ValidParamException ve = (ValidParamException) ex;
            return Result.result(ex.getCode(), msg).setData(ValidParamData.valueOf(ve.getTarget(), ve.getField()
                    , ve.getRejectValue(), ve.getMsg()));
        }
        return Result.result(ex.getCode(), msg);
    }

    /**
     * 不支持的请求方式
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        log.warn("HttpRequestMethodNotSupportedException:", ex);
        return Result.result(ExceptionCode.METHOD_NOT_ALLOWED);
    }


    /**
     * 参数类型不匹配
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result handlerMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
        log.warn("MethodArgumentTypeMismatchException:", ex);
        StringBuilder msg = new StringBuilder("参数[").append(ex.getName())
                .append("]的传入值[").append(ex.getValue())
                .append("]与预期的字段类型[").append(ex.getRequiredType().getName()).append("]不匹配");
        return Result.result(ExceptionCode.BASE_VALID_PARAM.build(msg.toString()));
    }

    /**
     * 参数错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result httpMessageNotReadableException(HttpMessageNotReadableException ex) {
        log.warn("HttpMessageNotReadableException:", ex);
        String msg = ex.getMessage();
        if (StrUtil.containsAny(msg, "Could not read document:")) {
            msg = String.format("无法正确的解析json类型的参数：%s", StrUtil.subBetween(msg, "Could not read document:", " at "));
        }
        if (StrUtil.containsAny(msg, "not one of the values accepted for Enum class:")) {
            msg = "枚举异常值";
        }
        return Result.result(ExceptionCode.BASE_VALID_PARAM.build(msg));
    }

    /**
     * spring 封装的参数验证异常， 在conttoller中没有写result参数时，会进入
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handlerMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        log.warn("MethodArgumentNotValidException:", ex);
        BindingResult bindingResult = ex.getBindingResult();
        FieldError fieldError = bindingResult.getFieldError();
        ValidParamData validParamData;
        String msg;
        if (fieldError != null) {
            validParamData = ValidParamData.valueOf(fieldError.getObjectName(), fieldError.getField(), fieldError.getRejectedValue()
                    , fieldError.getDefaultMessage());
            msg = validParamData.getMsg();
        } else {
            ObjectError globalError = bindingResult.getGlobalError();
            msg = globalError.getDefaultMessage();
            validParamData = new ValidParamData();
            validParamData.setTarget(globalError.getObjectName()).setMsg(msg);
        }
        return Result.result(ExceptionCode.BASE_VALID_PARAM.build(msg)).setData(validParamData);

    }

    /**
     * 参数缺失
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handlerMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
        log.warn("MissingServletRequestParameterException:", ex);
        String msg = StrUtil.format("缺少必须的[{}]类型的参数", ex.getParameterType());
        return Result.result(ExceptionCode.BASE_VALID_PARAM.build(msg));
    }

    /**
     * 参数异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(BindException.class)
    public Result bindException(BindException ex) {
        log.warn("BindException:", ex);
        try {
            String msg = ex.getBindingResult().getFieldError().getDefaultMessage();
            if (StrUtil.isNotEmpty(msg)) {
                return Result.result(ExceptionCode.BASE_VALID_PARAM.build(msg));
            }
        } catch (Exception ee) {
        }
        StringBuilder msg = new StringBuilder();
        List<FieldError> fieldErrors = ex.getFieldErrors();
        fieldErrors.forEach((oe) ->
                msg.append("参数:[").append(oe.getObjectName())
                        .append(".").append(oe.getField())
                        .append("]的传入值:[").append(oe.getRejectedValue()).append("]与预期的字段类型不匹配.")
        );
        return Result.result(ExceptionCode.BASE_VALID_PARAM.build(msg.toString()));
    }

    /**
     * 数据关联异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(DataIntegrityViolationException.class)
    public Result handlerDataIntegrityViolationException(DataIntegrityViolationException ex) {
        log.error("DataIntegrityViolationException:", ex);
        Result result = Result.result(ExceptionCode.INTERNAL_SERVER_ERROR.build("数据关联异常"));
        return setErrorData(result, ex);
    }

    /**
     * redis异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(RedisException.class)
    public Result handlerRedisException(RedisException ex) {
        log.error("RedisException:", ex);
        Result result = Result.result(ExceptionCode.INTERNAL_SERVER_ERROR.build("Redis异常"));
        return setErrorData(result, ex);
    }

    /**
     * 其他异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Result handlerException(Exception ex) {
        log.error("Exception:", ex);
        if (ex.getCause() instanceof BizException) {
            return this.handlerBizException((BizException) ex.getCause());
        }
        Result result = Result.result(ExceptionCode.INTERNAL_SERVER_ERROR);
        return setErrorData(result, ex);
    }

    private Result setErrorData(Result result, Exception ex) {
        if (SpringUtils.isDevOrTest()) {
            // 非开发环境不设置，避免被恶意猜测
            result.setData(ex.getMessage());
        }
        return result;
    }


}
